<template>
  <el-card class="b-table" :style="{ height: atTableHeight }" shadow="never">
    <el-table
      ref="elTableRef"
      v-loading="loading"
      :data="tableList"
      :stripe="config.stripe !== false ? true : false"
      :border="config.border"
      :span-method="config.spanMethod"
      :height="tableHeight"
      :max-height="config.maxHeight"
      @selection-change="handleSelectionChange"
      @expand-change="handleExpandChange"
      :empty-text="config.placeholder"
      :row-key="config.rowKey || 'id'"
      :tree-props="config.treeProps"
      :expand-row-keys="config.expandedRowKeys"
      :default-expand-all="config.defaultExpandAll"
    >
      <el-table-column
        ref="elTableColumnRef"
        v-bind="attrs(field)"
        v-for="field in fieldsData"
        :key="field.prop"
        :type="field.type"
        :reserve-selection="config.reserveSelection || false"
      >
        <el-table-column
          v-bind="attrs(field)"
          v-for="field in field.children"
          :key="field.prop"
        >
          <template v-if="!field.type" v-slot="scope">
            <template v-if="$scopedSlots[field.prop]">
              <slot
                :name="field.prop"
                :row="scope.row"
                :index="scope.$index"
              ></slot>
            </template>
            <template v-else>{{
              field.formatter(scope.row[field.prop], scope.row)
            }}</template>
          </template>
        </el-table-column>
        <template v-if="!field.type" v-slot="scope">
          <template v-if="$scopedSlots[field.prop]">
            <slot
              :name="field.prop"
              :row="scope.row"
              :index="scope.$index"
            ></slot>
          </template>
          <template v-else>{{
            field.formatter(scope.row[field.prop], scope.row)
          }}</template>
        </template>
      </el-table-column>
    </el-table>
    <pagination
      v-show="showPagination"
      :total="total"
      :page.sync="queryParams.pageNo"
      :limit.sync="queryParams.pageSize"
      @pagination="onPagination"
    />
  </el-card>
</template>

<script>
import { debounce, throttle } from "@/utils";
import { getUser } from "@/utils/auth";
import { formatterCurrency, numberFormatTime } from "@/utils/number";

export default {
  name: "bTable",
  props: {
    params: {
      type: Object,
      default: () => ({}),
    },
    config: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      loading: false,
      tableConfig: {},
      tableList: [],
      total: 0,
      atTableHeight: undefined,
      tableHeight: undefined,
      immediate: undefined,
      resizeObserver: null,
      elTableColumnProps: {},
      throttleGetTableList: null,
    };
  },
  watch: {
    params: {
      handler() {
        this.queryParams = { ...this.params };
      },
      immediate: true,
      deep: true,
    },
    config: {
      handler(val, old) {
        // 是否立即执行加载数据
        if (this.immediate === undefined) {
          this.immediate = val.immediate ?? true;
        }
        if (
          val?.["api"] !== old?.["api"] ||
          val?.["fields"] !== old?.["fields"]
        ) {
          this.getTableList();
        } else {
          this.throttleGetTableList();
        }
        this.$nextTick(() => {
          this.$refs?.elTableRef?.doLayout();
        });
      },
      immediate: true,
      deep: true,
    },
    "config.list": {
      handler(val, old) {
        if (val != undefined) this.getTableList();
      },
      immediate: true,
      deep: true,
    },
    tableList: {
      handler(val) {
        // 当list数据>0时默认为加载完成
        // if (val?.length > 0) {
        this.$emit("loadEnd", val ?? []);
        // }
      },
      // immediate: true,
      deep: true,
    },
  },
  computed: {
    attrs() {
      return (field) => {
        let res = {};
        for (let key in this.elTableColumnProps) {
          res[key] = field[key] ?? this.elTableColumnProps[key];
        }
        return res;
      };
    },
    otherSlots() {
      const obj = {};
      for (const key in this.$scopedSlots) {
        if (!this.config.fields[key]) {
          obj[key] = this.$scopedSlots[key];
        }
      }
      return obj;
    },
    fieldsData() {
      let map = this.config?.fields ?? {};
      if (Object.prototype.toString.call(map) !== "[object Map]") {
        map = new Map(Object.entries(map));
      }
      let arr = [];
      for (const [key, value] of map) {
        let obj = { ...value, prop: key.toString() };
        obj = this.getFieldData(obj);
        if (obj.children) {
          const children = [];
          for (const Ckey in obj.children) {
            let Cobj = { ...obj.children[Ckey], prop: Ckey };
            Cobj = this.getFieldData(Cobj);
            if (!Cobj.hide) children.push(Cobj);
          }
          if (!obj.hide) arr.push({ ...obj, children: children });
        } else {
          if (!obj.hide) arr.push(obj);
        }
      }
      return arr;
    },
    /** 是否显示分页 */
    showPagination() {
      if (this.config.pagination == "auto") {
        // 大于一页才显示分页
        return this.total > this.params.pageSize;
      } else {
        // 默认有数据就显示分页
        return this.total > 0;
      }
    },
  },
  mounted() {
    if (this.config.height == undefined) this.initObserver();
    this.elTableColumnProps = { ...this.$refs?.elTableColumnRef?.[0]?.$props };
    this.throttleGetTableList = throttle(this.getTableList, 3000);
  },
  beforeDestroy() {
    if (this.resizeObserver) {
      const search = this.$el.parentElement?.querySelector(".b-table-search"); // 搜索区域
      const operation = this.$el.parentElement?.querySelector(
        ".b-table-operation"
      ); // 操作区域
      const pagination = this.$el.parentElement?.querySelector(
        ".pagination-container"
      ); // 分页区域
      if (search) this.resizeObserver.disconnect(search);
      if (operation) this.resizeObserver.disconnect(operation);
      if (pagination) this.resizeObserver.disconnect(pagination);
      this.resizeObserver = null;
    }
  },
  methods: {
    getFieldData(obj) {
      if (obj.prop === "index") {
        obj.type = "index";
        obj.width = obj.width ?? "50px";
        obj.label = obj.label ?? "序号";
        obj.fixed = obj.fixed ?? "left";
      }
      if (obj.prop === "selection") {
        obj.type = "selection";
        obj.width = obj.width ?? "55px";
        obj.fixed = obj.fixed ?? "left";
        obj.selectable =
          obj.selectable && obj.selectable instanceof Function
            ? obj.selectable
            : () => true;
        obj.reserveSelection = obj.reserveSelection ?? false;
      }
      if (obj.formatter) {
        if (obj.formatter === "currency")
          obj.formatter = (val) => {
            return formatterCurrency(val);
          };
        if (obj.formatter === "timeLong")
          obj.formatter = (val) => {
            return numberFormatTime(val);
          };
      } else {
        obj.formatter = (val) => {
          return val;
        };
      }
      obj.align = obj.align ?? "center";
      return obj;
    },
    initObserver() {
      const _this = this;
      const search = this.$el.parentElement?.querySelector(".b-table-search"); // 搜索区域
      const operation = this.$el.parentElement?.querySelector(
        ".b-table-operation"
      ); // 操作区域
      const pagination = this.$el.parentElement?.querySelector(
        ".pagination-container"
      ); // 分页区域
      this.resizeObserver = new ResizeObserver((entries) => {
        entries.forEach((entry) => {
          if (
            entry?.target?.classList?.contains("pagination-container") &&
            pagination.offsetHeight > 0
          ) {
            _this.setTableHeight();
          } else {
            _this.setbTableHeight();
          }
        });
      });
      if (search) this.resizeObserver.observe(search, { attributes: true });
      if (operation)
        this.resizeObserver.observe(operation, { attributes: true });
      if (pagination)
        this.resizeObserver.observe(pagination, { attributes: true });
    },
    setTableHeight() {
      if (this.config.height) return (this.tableHeight = this.config.height);
      if (this.config.height == undefined) {
        const pagination = this.$el.parentElement?.querySelector(
          ".pagination-container"
        ); // 分页区域
        if (pagination)
          this.tableHeight = `calc(100% - ${pagination.offsetHeight}px)`;
      }
    },
    setbTableHeight() {
      if (this.config.height) return (this.atTableHeight = this.config.height);
      if (this.config.height == undefined) {
        const search =
          this.$el.parentElement?.querySelector(".b-table-search"); // 搜索区域
        const operation = this.$el.parentElement?.querySelector(
          ".b-table-operation"
        ); // 操作区域
        const tabs = this.$el.parentElement?.querySelector(".b-tabs"); // 标签页区域
        if (search || operation || tabs)
          this.atTableHeight = `calc(100% - ${
            (search?.offsetHeight ?? 0) +
            (operation?.offsetHeight ?? 0) +
            (tabs?.offsetHeight ?? 0) +
            16 // search-margin-bottom
          }px)`;
        else this.atTableHeight = `calc(100%)`;
      }
    },
    onPagination() {
      this.getTableList();
      this.$nextTick(() => {
        // 调到底部
        // this.$refs.elTableRef.bodyWrapper.scrollTop = this.$refs.elTableRef.bodyWrapper.scrollHeight;
        // 调到顶部
        this.$refs.elTableRef.bodyWrapper.scrollTop = 0;
      });
    },
    getTableList() {
      if (this.config?.list) {
        this.tableList = this.config?.list;
        return;
      }
      if (this.config?.api && this.immediate !== false) {
        this.loading = true;
        const queryParams = {};
        // 排除不需要的参数
        for (const key in this.queryParams) {
          if (this.config?.exclude?.includes(key)) {
            continue;
          }
          queryParams[key] = this.queryParams[key];
        }
        // 默认选中当前登录用户的所属区域 (noArea 不按区域隔离数据，默认为空)
        let areaId = getUser()?.areaId ?? "";
        if (areaId && !queryParams["areaId"] && this.config?.noArea !== true) {
          queryParams["areaId"] = areaId;
          this.$emit("update:params", queryParams);
        }
        this.config
          ?.api(queryParams)
          .then((res) => {
            // 无分页
            if (this.config.pagination === false) {
              this.tableList = res.data ?? [];
              this.total = 0;
            } else {
              if (
                this.queryParams?.pageNo == 0 ||
                this.queryParams?.pageSize == 0
              ) {
                this.tableList = [];
                this.total = 0;
              } else {
                this.tableList = res.data.list;
                this.total = res.data.total;
              }
            }
            this.loading = false;
          })
          .catch((err) => {
            this.loading = false;
            if (err.code === 1018002) {
              this.$aliLogin({
                bind: true,
                onSuccess: () => {
                  this.getTableList();
                },
              });
            }
          });
      }
      this.immediate = true;
    },
    refresh() {
      this.getTableList();
    },
    clear() {
      this.tableList = [];
      this.total = 0;
    },
    handleSelectionChange(val) {
      this.$emit("selection-change", val);
    },
    handleExpandChange(val) {
      this.$emit("expand-change", val);
    },
  },
};
</script>

<style lang="scss" scoped>
:deep(.el-card__body) {
  height: 100%;
}

// 解决左右 对不齐 的情况
// el-table 左右有列固定时，fixed为left和right时，行未对齐解决办法
// * 产生原因： el-table底部有滚动条，固定列底部没有滚动条
// * 解决办法： 在el-table的表格中设置padding-bottom的内边距跟滚动条等高
:deep(.el-table) {
  .el-table__fixed,
  .el-table__fixed-left,
  .el-table__fixed-right {
    // 10 是我这里全局定义的滚动条的高度
    height: calc(100% - 10px) !important;
  }

  .el-table__fixed-body-wrapper {
    height: calc(100% - 43px) !important;
  }

  .el-table__fixed-body-wrapper .el-table__body {
    /*滚动条高度*/
    // 10 是我这里全局定义的滚动条的高度
    padding-bottom: 10px !important;
  }

  .el-table__body {
    .red {
      color: #ff4058;
    }
  }
}
</style>
